/*
 * Copyright 2011 Alibaba.com All right reserved. This software is the
 * confidential and proprietary information of Alibaba.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Alibaba.com.
 */
package com.alibaba.dubbo.governance.web.governance.module.screen;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.governance.service.ConsumerService;
import com.alibaba.dubbo.governance.service.OwnerService;
import com.alibaba.dubbo.governance.service.ProviderService;
import com.alibaba.dubbo.governance.service.RouteService;
import com.alibaba.dubbo.governance.web.common.module.screen.Restful;
import com.alibaba.dubbo.registry.common.domain.Consumer;
import com.alibaba.dubbo.registry.common.domain.Provider;
import com.alibaba.dubbo.registry.common.domain.Route;
import com.alibaba.dubbo.registry.common.route.ParseUtils;
import com.alibaba.dubbo.registry.common.route.RouteRule;
import com.alibaba.dubbo.registry.common.route.RouteUtils;
import com.alibaba.dubbo.registry.common.util.Tool;

/**
 * Providers. URI: /services/$service/routes
 * 
 * @author ding.lid
 * @author william.liangf
 * @author tony.chenl
 */
public class Routes extends Restful {

	private static final int MAX_RULE_LENGTH = 1000;

	@Autowired
	private RouteService routeService;

	@Autowired
	private ProviderService providerService;

	@Autowired
	private ConsumerService consumerService;

	static String[][] when_names = { { "method", "method", "unmethod" }, { "consumer.application", "consumerApplication", "unconsumerApplication" },
			{ "consumer.cluster", "consumerCluster", "unconsumerCluster" }, { "consumer.host", "consumerHost", "unconsumerHost" },
			{ "consumer.version", "consumerVersion", "unconsumerVersion" }, { "consumer.group", "consumerGroup", "unconsumerGroup" }, };

	static String[][] then_names = {
			{ "provider.application", "providerApplication", "unproviderApplication" },
			{ "provider.cluster", "providerCluster", "unproviderCluster" }, // 要校验Cluster是否存在
			{ "provider.host", "providerHost", "unproviderHost" }, { "provider.protocol", "providerProtocol", "unproviderProtocol" },
			{ "provider.port", "providerPort", "unproviderPort" }, { "provider.version", "providerVersion", "unproviderVersion" },
			{ "provider.group", "providerGroup", "unproviderGroup" } };

	/**
	 * 路由模块首页
	 * 
	 * @param context
	 */
	public void index(Map<String, Object> context) {
		String service = (String) context.get("service");
		String address = (String) context.get("address");
		address = Tool.getIP(address);
		List<Route> routes;
		if (service != null && service.length() > 0 && address != null && address.length() > 0) {
			routes = routeService.findByServiceAndAddress(service, address);
		} else if (service != null && service.length() > 0) {
			routes = routeService.findByService(service);
		} else if (address != null && address.length() > 0) {
			routes = routeService.findByAddress(address);
		} else {
			routes = routeService.findAll();
		}
		context.put("routes", routes);
	}

	/**
	 * 显示路由详细信息
	 * 
	 * @param context
	 */
	public void show(Map<String, Object> context) {
		try {
			Route route = routeService.findRoute(Long.parseLong((String) context.get("id")));

			if (route == null) {
				throw new IllegalArgumentException("The route is not existed.");
			}
			if (route.getService() != null && !route.getService().isEmpty()) {
				context.put("service", route.getService());
			}

			RouteRule routeRule = RouteRule.parse(route);

			@SuppressWarnings("unchecked")
			Map<String, RouteRule.MatchPair>[] paramArray = new Map[] { routeRule.getWhenCondition(), routeRule.getThenCondition() };
			String[][][] namesArray = new String[][][] { when_names, then_names };

			for (int i = 0; i < paramArray.length; ++i) {
				Map<String, RouteRule.MatchPair> param = paramArray[i];
				String[][] names = namesArray[i];
				for (String[] name : names) {
					RouteRule.MatchPair matchPair = param.get(name[0]);
					if (matchPair == null) {
						continue;
					}

					if (!matchPair.getMatches().isEmpty()) {
						String m = RouteRule.join(matchPair.getMatches());
						context.put(name[1], m);
					}
					if (!matchPair.getUnmatches().isEmpty()) {
						String u = RouteRule.join(matchPair.getUnmatches());
						context.put(name[2], u);
					}
				}
			}
			context.put("route", route);
			context.put("methods", CollectionUtils.sort(new ArrayList<String>(providerService.findMethodsByService(route.getService()))));
		} catch (ParseException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 载入新增路由页面
	 * 
	 * @param context
	 */
	public void add(Map<String, Object> context) {
		String service = (String) context.get("service");
		if (service != null && service.length() > 0 && !service.contains("*")) {
			context.put("service", service);
			context.put("methods", CollectionUtils.sort(new ArrayList<String>(providerService.findMethodsByService(service))));
		} else {
			List<String> serviceList = Tool.sortSimpleName(new ArrayList<String>(providerService.findServices()));
			context.put("serviceList", serviceList);
		}

		if (context.get("input") != null)
			context.put("input", context.get("input"));

	}

	/**
	 * 载入批量新增路由页面
	 * 
	 * @param context
	 */
	public void globalAdd(Map<String, Object> context) {
		List<String> serviceList = Tool.sortSimpleName(new ArrayList<String>(providerService.findServices()));
		context.put("serviceList", serviceList);

		if (context.get("input") != null)
			context.put("input", context.get("input"));

	}

	/**
	 * 载入修改路由页面
	 * 
	 * @param context
	 */
	public void edit(Map<String, Object> context) {
		add(context);
		show(context);
	}

	static void checkService(String service) {
		if (service.contains(","))
			throw new IllegalStateException("service(" + service + ") contain illegale ','");

		String interfaceName = service;
		int gi = interfaceName.indexOf("/");
		if (gi != -1)
			interfaceName = interfaceName.substring(gi + 1);
		int vi = interfaceName.indexOf(':');
		if (vi != -1)
			interfaceName = interfaceName.substring(0, vi);

		if (interfaceName.indexOf('*') != -1 && interfaceName.indexOf('*') != interfaceName.length() - 1) {
			throw new IllegalStateException("service(" + service + ") only allow 1 *, and must be last char!");
		}
	}

	/**
	 * 保存路由信息到数据库中
	 * 
	 * @param context
	 * @return
	 */
	public boolean create(Map<String, Object> context) {
		String name = (String) context.get("name");
		String service = (String) context.get("service");
		if (StringUtils.isNotEmpty(service) && StringUtils.isNotEmpty(name)) {
			checkService(service);

			Map<String, String> when_name2valueList = new HashMap<String, String>();
			Map<String, String> notWhen_name2valueList = new HashMap<String, String>();
			for (String[] names : when_names) {
				when_name2valueList.put(names[0], (String) context.get(names[1]));
				notWhen_name2valueList.put(names[0], (String) context.get(names[2])); // value不为null的情况，这里处理，后面会保证
			}

			Map<String, String> then_name2valueList = new HashMap<String, String>();
			Map<String, String> notThen_name2valueList = new HashMap<String, String>();
			for (String[] names : then_names) {
				then_name2valueList.put(names[0], (String) context.get(names[1]));
				notThen_name2valueList.put(names[0], (String) context.get(names[2]));
			}

			RouteRule routeRule = RouteRule.createFromNameAndValueListString(when_name2valueList, notWhen_name2valueList, then_name2valueList,
					notThen_name2valueList);

			if (routeRule.getThenCondition().isEmpty()) {
				context.put("message", getMessage("Add route error! then is empty."));
				return false;
			}

			String matchRule = routeRule.getWhenConditionString();
			String filterRule = routeRule.getThenConditionString();

			// 限制表达式的长度
			if (matchRule.length() > MAX_RULE_LENGTH) {
				context.put("message", getMessage("When rule is too long!"));
				return false;
			}
			if (filterRule.length() > MAX_RULE_LENGTH) {
				context.put("message", getMessage("Then rule is too long!"));
				return false;
			}

			Route route = new Route();
			route.setService(service);
			route.setName(name);
			route.setUsername((String) context.get("operator"));
			route.setOperator((String) context.get("operatorAddress"));
			route.setRule(routeRule.toString());
			if (StringUtils.isNotEmpty((String) context.get("priority"))) {
				route.setPriority(Integer.parseInt((String) context.get("priority")));
			}

			List<Route> currentRouteList = routeService.findByService(service);

			if (currentRouteList == null) {
				routeService.createRoute(route);
			} else if (!currentRouteList.contains(route)) {
				routeService.createRoute(route);
			}
		}

		return true;
	}

	/**
	 * 保存路由信息到数据库中
	 * 
	 * @param context
	 * @return
	 */
	public boolean globalCreate(Map<String, Object> context) {
		String name = (String) context.get("name");
		String services = (String) context.get("services");
		List<String> serviceList = null;

		if (StringUtils.isBlank(services)) {
			serviceList = Tool.sortSimpleName(new ArrayList<String>(providerService.findServices()));
		} else {
			serviceList = Arrays.asList(services.split(","));
		}

		if (serviceList != null) {
			for (String service : serviceList) {
				checkService(service);
				Map<String, String> when_name2valueList = new HashMap<String, String>();
				Map<String, String> notWhen_name2valueList = new HashMap<String, String>();

				for (String[] names : when_names) {
					when_name2valueList.put(names[0], (String) context.get(names[1]));
					notWhen_name2valueList.put(names[0], (String) context.get(names[2])); // value不为null的情况，这里处理，后面会保证
				}

				Map<String, String> then_name2valueList = new HashMap<String, String>();
				Map<String, String> notThen_name2valueList = new HashMap<String, String>();
				for (String[] names : then_names) {
					then_name2valueList.put(names[0], (String) context.get(names[1]));
					notThen_name2valueList.put(names[0], (String) context.get(names[2]));
				}

				RouteRule routeRule = RouteRule.createFromNameAndValueListString(when_name2valueList, notWhen_name2valueList, then_name2valueList,
						notThen_name2valueList);

				if (routeRule.getThenCondition().isEmpty()) {
					context.put("message", getMessage("Add route error! then is empty."));
					return false;
				}

				String matchRule = routeRule.getWhenConditionString();
				String filterRule = routeRule.getThenConditionString();

				// 限制表达式的长度
				if (matchRule.length() > MAX_RULE_LENGTH) {
					context.put("message", getMessage("When rule is too long!"));
					return false;
				}
				if (filterRule.length() > MAX_RULE_LENGTH) {
					context.put("message", getMessage("Then rule is too long!"));
					return false;
				}

				Route route = new Route();
				route.setService(service);
				route.setName(name);
				route.setUsername((String) context.get("operator"));
				route.setOperator((String) context.get("operatorAddress"));
				route.setRule(routeRule.toString());
				route.setGlobal(true);
				boolean autoStartup = ((String) context.get("autoStartup") != null) ? Boolean.valueOf((String) context.get("autoStartup")) : false;
				route.setAutoStartup(autoStartup);

				if (StringUtils.isNotEmpty((String) context.get("priority"))) {
					route.setPriority(Integer.parseInt((String) context.get("priority")));
				}

				List<Route> currentRouteList = routeService.findByService(service);

				if (currentRouteList == null) {
					routeService.createRoute(route);
				} else if (!currentRouteList.contains(route)) {
					routeService.createRoute(route);
				}
			}
		}

		return true;
	}

	/**
	 * 保存更新数据到数据库中
	 * 
	 * @param context
	 * @return
	 */
	public boolean update(Map<String, Object> context) {
		String idStr = (String) context.get("id");
		if (idStr != null && idStr.length() > 0) {
			String[] blacks = (String[]) context.get("black");
			boolean black = false;
			if (blacks != null && blacks.length > 0) {
				black = true;
			}

			Route oldRoute = routeService.findRoute(Long.valueOf(idStr));
			if (null == oldRoute) {
				context.put("message", getMessage("NoSuchRecord"));
				return false;
			}
			// 判断参数，拼凑rule
			if (StringUtils.isNotEmpty((String) context.get("name"))) {
				String service = oldRoute.getService();
				if (context.get("operator") == null) {
					context.put("message", getMessage("HaveNoServicePrivilege", service));
					return false;
				}

				Map<String, String> when_name2valueList = new HashMap<String, String>();
				Map<String, String> notWhen_name2valueList = new HashMap<String, String>();
				for (String[] names : when_names) {
					when_name2valueList.put(names[0], (String) context.get(names[1]));
					notWhen_name2valueList.put(names[0], (String) context.get(names[2])); // value不为null的情况，这里处理，后面会保证
				}

				Map<String, String> then_name2valueList = new HashMap<String, String>();
				Map<String, String> notThen_name2valueList = new HashMap<String, String>();
				for (String[] names : then_names) {
					then_name2valueList.put(names[0], (String) context.get(names[1]));
					notThen_name2valueList.put(names[0], (String) context.get(names[2]));
				}

				RouteRule routeRule = RouteRule.createFromNameAndValueListString(when_name2valueList, notWhen_name2valueList, then_name2valueList,
						notThen_name2valueList);

				RouteRule result = null;
				if (black) {
					RouteRule.MatchPair matchPair = routeRule.getThenCondition().get("black");
					Map<String, RouteRule.MatchPair> then = null;
					if (null == matchPair) {
						matchPair = new RouteRule.MatchPair();
						then = new HashMap<String, RouteRule.MatchPair>();
						then.put("black", matchPair);
					} else {
						matchPair.getMatches().clear();
					}
					matchPair.getMatches().add(String.valueOf(black));
					result = RouteRule.copyWithReplace(routeRule, null, then);
				}

				if (result == null) {
					result = routeRule;
				}

				if (result.getThenCondition().isEmpty()) {
					context.put("message", getMessage("Update route error! then is empty."));
					return false;
				}

				String matchRule = result.getWhenConditionString();
				String filterRule = result.getThenConditionString();

				// 限制表达式的长度
				if (matchRule.length() > MAX_RULE_LENGTH) {
					context.put("message", getMessage("When rule is too long!"));
					return false;
				}
				if (filterRule.length() > MAX_RULE_LENGTH) {
					context.put("message", getMessage("Then rule is too long!"));
					return false;
				}

				int priority = 0;
				if (StringUtils.isNotEmpty((String) context.get("priority"))) {
					priority = Integer.parseInt((String) context.get("priority"));
				}

				Route route = new Route();
				route.setRule(result.toString());
				route.setService(service);
				route.setPriority(priority);
				route.setName((String) context.get("name"));
				route.setUsername((String) context.get("operator"));
				route.setOperator((String) context.get("operatorAddress"));
				route.setId(Long.valueOf(idStr));
				route.setPriority(Integer.parseInt((String) context.get("priority")));
				route.setEnabled(oldRoute.isEnabled());
				routeService.updateRoute(route);

				Set<String> usernames = new HashSet<String>();
				usernames.add((String) context.get("operator"));
				usernames.add(route.getUsername());
				// RelateUserUtils.addOwnersOfService(usernames,
				// route.getService(), ownerDAO);

				Map<String, Object> params = new HashMap<String, Object>();
				params.put("action", "update");
				params.put("route", route);

			} else {
				context.put("message", getMessage("MissRequestParameters", "name"));
			}
		} else {
			context.put("message", getMessage("MissRequestParameters", "id"));
		}

		return true;
	}

	/**
	 * 删除指定ID的route规则
	 * 
	 * @param ids
	 * @return
	 */
	public boolean delete(Long[] ids, Map<String, Object> context) {
		for (Long id : ids) {
			routeService.deleteRoute(id);
		}

		return true;
	}

	/**
	 * 启用指定ID的route规则（可以批量处理）
	 * 
	 * @param ids
	 * @return
	 */
	public boolean enable(Long[] ids, Map<String, Object> context) {
		for (Long id : ids) {
			routeService.enableRoute(id);
		}

		return true;
	}

	/**
	 * 禁用指定ID的route规则（可以批量处理）
	 * 
	 * @param ids
	 * @return
	 */
	public boolean disable(Long[] ids, Map<String, Object> context) {
		for (Long id : ids) {
			routeService.disableRoute(id);
		}

		return true;
	}

	/**
	 * 选择消费者
	 * 
	 * @param context
	 */
	public void routeselect(Map<String, Object> context) {
		long rid = Long.valueOf((String) context.get("id"));
		context.put("id", rid);

		Route route = routeService.findRoute(rid);
		if (route == null) {
			throw new IllegalStateException("Route(id=" + rid + ") is not existed!");
		}

		context.put("route", route);
		// 获取数据
		List<Consumer> consumers = consumerService.findByService(route.getService());
		context.put("consumers", consumers);

		Map<String, Boolean> matchRoute = new HashMap<String, Boolean>();
		for (Consumer c : consumers) {
			matchRoute.put(c.getAddress(), RouteUtils.matchRoute(c.getAddress(), null, route, null));
		}
		context.put("matchRoute", matchRoute);
	}

	public void preview(Map<String, Object> context) throws Exception {
		String rid = (String) context.get("id");
		String consumerid = (String) context.get("cid");

		if (StringUtils.isEmpty(rid)) {
			context.put("message", getMessage("MissRequestParameters", "id"));
		}

		Map<String, String> serviceUrls = new HashMap<String, String>();
		Route route = routeService.findRoute(Long.valueOf(rid));
		if (null == route) {
			context.put("message", getMessage("NoSuchRecord"));
		}
		List<Provider> providers = providerService.findByService(route.getService());
		if (providers != null) {
			for (Provider p : providers) {
				serviceUrls.put(p.getUrl(), p.getParameters());
			}
		}
		if (StringUtils.isNotEmpty(consumerid)) {
			Consumer consumer = consumerService.findConsumer(Long.valueOf(consumerid));
			if (null == consumer) {
				context.put("message", getMessage("NoSuchRecord"));
			}
			Map<String, String> result = RouteUtils.previewRoute(consumer.getService(), consumer.getAddress(), consumer.getParameters(), serviceUrls, route,
					null, null);
			context.put("route", route);
			context.put("consumer", consumer);
			context.put("result", result);
		} else {
			String address = (String) context.get("address");
			String service = (String) context.get("service");

			Map<String, String> result = RouteUtils.previewRoute(service, address, null, serviceUrls, route, null, null);
			context.put("route", route);

			Consumer consumer = new Consumer();
			consumer.setService(service);
			consumer.setAddress(address);
			context.put("consumer", consumer);
			context.put("result", result);
		}

	}

	/**
	 * 添加与服务相关的Owner
	 * 
	 * @param usernames 用于添加的用户名
	 * @param serviceName 不含通配符
	 */
	public static void addOwnersOfService(Set<String> usernames, String serviceName, OwnerService ownerDAO) {
		List<String> serviceNamePatterns = ownerDAO.findAllServiceNames();
		for (String p : serviceNamePatterns) {
			if (ParseUtils.isMatchGlobPattern(p, serviceName)) {
				List<String> list = ownerDAO.findUsernamesByServiceName(p);
				usernames.addAll(list);
			}
		}
	}

	/**
	 * 添加与服务模式相关的Owner
	 * 
	 * @param usernames 用于添加的用户名
	 * @param serviceNamePattern 服务模式，Glob模式
	 */
	public static void addOwnersOfServicePattern(Set<String> usernames, String serviceNamePattern, OwnerService ownerDAO) {
		List<String> serviceNamePatterns = ownerDAO.findAllServiceNames();
		for (String p : serviceNamePatterns) {
			if (ParseUtils.hasIntersection(p, serviceNamePattern)) {
				List<String> list = ownerDAO.findUsernamesByServiceName(p);
				usernames.addAll(list);
			}
		}
	}
}
